memory leak字面上翻譯就叫做記憶體洩漏,記憶體洩漏會造成什麼問題?大家應該有那種經驗,開太多chrome分頁,電腦開始卡卡的,嚴重點直接當掉,memory leaky的概念類似這樣,程式在運作的時候會佔用記憶體,沒有用到的記憶體如果沒被即時釋放,記憶體佔用就會越來越高,然後你的程式就會崩潰了!
在了解memory leak之前,先來認識javasScript的垃圾回收機制(garbage collection),在記憶體沒有被用到的時候應該要被釋放,但在下面幾種情境下就會造成記憶體洩漏
function test(){
name = "bill"
}
function parent(){
var count = 0
return function(){
console.log('count'+count)
}
}
3.計時器:setInterval、setTimeout等等沒使用時,如果未清除還是會持續佔用記憶體,所以應該使用clearInterval以及clearTimeout來清除計時器
4.DOM被移除時,監聽事件未被移除(部分老舊瀏覽器
document.getElementById('copy').addEventListener('click',copyFunction)
const target=document.getElementById('copy')
target.parentNode.removeChild(target);
如果有用到計時器但沒有在component銷毀的時候,一併清除計時器的話就會造成記憶體洩漏的問題,然後如果你有很多個計時器的話,瀏覽器應該會崩潰
為了展示計時器忘記清除的狀況下造成的記憶體洩漏問題,寫了一個計時器每秒會自動加一,剛好遇到一個問題,那就是我的數字永遠都停在1,以為setInterval只會執行一遍,但如果看console.log,會發現其實每秒都在執行,只是count永遠都是1
export default () => {
const [count, setCount] = useState(0)
useEffect(() => {
setInterval(() => {
setCount(count+1)
}, 1000);
}, [])
return (
<div>count:{count}</div>
)
}
為什麼會這樣?因為setInterval取到的count是最初始的0,是的,這是閉包的陷阱,如果要解決這個問題可以在setState傳入一個function,count=>count+1,該函式就可以取得最新的count
useEffect(() => {
setInterval(() => {
setCount(count=>count+1)
}, 1000);
}, [])
或者將count傳入useEffect,當count變化時就會觸發useEffect
useEffect(() => {
setInterval(() => {
setCount(count + 1)
}, 1000);
}, [count])
當計時器終於正常運作的時候,我在外部component寫了一個判斷,三秒過後變數show會變成false,此時就會銷毀計時器component
{show ? <MemoryLeak /> :null}
三秒過後會發現計時器消失了的同時,也出現了這樣的錯誤,看到關鍵字 memory leak
要解決這樣的問題,就要記得在useeffect內回傳clean up函式,就會在component銷毀時清除計時器
useEffect(() => {
const timer = setInterval(() => {
setCount(count=>count+1)
}, 1000);
return () =>{
clearInterval(timer)
}
}, [])
還有兩種比較常見的memory leak
假設寫了setTimeout 設定五秒後setState,但component在五秒之前就已經銷毀了,此時五秒一到執行setState就會造成memory leak
有個情境是call 拿到資料後setState,如果用了非同步請求,在api比較慢的狀況下,response還沒回來時,component被銷毀後response才回來觸發了setState,造成memory leak
會造成react memory leak通常就是在已經銷毀的component上進行setState
解決settimeout、setinterval造成的memory leak
解決非同步造成的memory leak